<template>
  <div
    v-drag
    :class="['floatBox', { hide: hide }]"
    :style="{ top: top + 'px', left: left + 'px' }"
    @mouseenter="handleEnter"
    @mouseleave="handleLeave"
    ref="floatBoxRef"
  >
    <slot></slot>
  </div>
</template>

<script lang="ts">
import {
  defineComponent,
  onBeforeMount,
  onMounted,
  onUnmounted,
  reactive,
  toRefs,
} from "vue";

export default defineComponent({
  name: "TyFloat",
  setup() {
    const state = reactive({
      floatBoxRef: {} as HTMLElement,
      hide: false,
      range: 10,
      top: 0,
      left: 0,
      init: {} as NodeJS.Timeout,
      topHide: false,
      bottomHide: false,
      leftHide: false,
      rightHide: false,
    });

    // 鼠标进入
    const handleEnter = () => {
      let a = state.floatBoxRef;
      let width = window.innerWidth - (a.offsetLeft + a.offsetWidth);
      let height = window.innerHeight - (a.offsetTop + a.offsetHeight);
      if (state.left <= 0) {
        state.left = 0;
      } else if (width <= 0) {
        state.left = window.innerWidth - a.offsetWidth;
      } else if (state.top <= 0) {
        state.top = 0;
      } else if (height <= 0) {
        state.top = window.innerHeight - a.offsetHeight;
      }
      state.hide = false;
      state.topHide = false;
      state.bottomHide = false;
      state.leftHide = false;
      state.rightHide = false;
    };

    // 鼠标离开
    const handleLeave = () => {
      let a = state.floatBoxRef;
      // 当拖拽窗口进入隐藏范围内
      let width = window.innerWidth - (a.offsetLeft + a.offsetWidth);
      let height = window.innerHeight - (a.offsetTop + a.offsetHeight);
      if (
        a.offsetLeft <= state.range ||
        width <= state.range ||
        a.offsetTop <= state.range ||
        height <= state.range
      ) {
        // 分别计算上下,左右那边较近,再拿较近的那边比较
        let minWidth = a.offsetLeft > width ? width : a.offsetLeft;
        let minHeight = a.offsetTop > height ? height : a.offsetTop;
        // 比较左右与上下的距离
        if (minWidth <= minHeight) {
          if (a.offsetLeft >= window.innerWidth / 2) {
            state.left = window.innerWidth - a.offsetWidth / 2;
            state.rightHide = true;
          } else {
            state.left = -a.offsetWidth / 2;
            state.leftHide = true;
          }
          state.top = a.offsetTop;
        } else {
          if (a.offsetTop >= window.innerHeight / 2) {
            state.top = window.innerHeight - a.offsetHeight / 2;
            state.bottomHide = true;
          } else {
            state.top = -a.offsetHeight / 2;
            state.topHide = true;
          }
          state.left = a.offsetLeft;
        }
        state.hide = true;
      }
    };

    // 初始化
    const handleInit = () => {
      state.init = setTimeout(() => {
        state.top = (window.innerHeight - state.floatBoxRef.offsetHeight) / 2;
        state.left = window.innerWidth - state.floatBoxRef.offsetWidth / 2;
        clearTimeout(state.init);
      }, 100);
      state.hide = true;
      state.rightHide = true;
    };

    // 窗口变化
    const resizeWindow = () => {
      state.init = setTimeout(() => {
        let a = state.floatBoxRef;
        if (state.topHide) {
          state.top = -a.offsetHeight / 2;
        } else if (state.bottomHide) {
          state.top = window.innerHeight - a.offsetHeight / 2;
        } else if (state.leftHide) {
          state.left = -a.offsetWidth / 2;
        } else if (state.rightHide) {
          state.left = window.innerWidth - a.offsetWidth / 2;
        }
        clearTimeout(state.init);
      }, 100);
      state.hide = true;
    };

    onBeforeMount(() => {
      // 挂载窗口变化事件
      window.addEventListener("resize", (event) => resizeWindow());
    });
    onUnmounted(() => {
      // 销毁窗口变化事件
      window.removeEventListener("resize", (event) => resizeWindow());
    });

    onMounted(() => {
      handleInit();
    });

    return {
      ...toRefs(state),
      handleLeave,
      handleEnter,
    };
  },
});
</script>

<style scoped>
.floatBox {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 6666;
  user-select: none;
}
.floatBox:hover {
  cursor: move;
}
.hide {
  transition: 0.3s linear;
}
</style>